package com.bingxue.edu.management.system.service;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.HttpUtil;
import com.bingxue.edu.common.enums.CommonStatusEnum;
import com.bingxue.edu.common.enums.StorageTypeEnum;
import com.bingxue.edu.framework.common.util.CheckUtils;
import com.bingxue.edu.framework.common.util.SpringWebUtils;
import com.bingxue.edu.framework.common.util.ValidationUtils;
import com.bingxue.edu.framework.mybatis.base.BaseService;
import com.bingxue.edu.management.system.mapper.StorageMapper;
import com.bingxue.edu.management.system.model.entity.Storage;
import com.bingxue.edu.management.system.model.req.StorageReq;
import com.bingxue.edu.management.system.util.ValidateGroup;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.x.file.storage.core.FileStorageProperties;
import org.dromara.x.file.storage.core.FileStorageService;
import org.dromara.x.file.storage.core.FileStorageServiceBuilder;
import org.dromara.x.file.storage.core.platform.FileStorage;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import static com.bingxue.edu.management.system.model.entity.table.StorageTableDef.STORAGE;

/**
 * 存储配置业务实现
 *
 * @author panbo
 * @since 2024/07/22 23:33
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class StorageService extends BaseService<StorageMapper, Storage> {

    @Lazy
    @Resource
    private FileService fileService;
    @Lazy
    @Resource
    private FileStorageService fileStorageService;

    /**
     * 创建存储
     */
    public void create(StorageReq storageReq) {
        CheckUtils.throwIf(storageReq.getDefaultStorage() && this.isDefaultExists(null), "请先取消原有默认存储");
        String code = storageReq.getCode();
        CheckUtils.throwIf(this.isCodeExists(code, null), "新增失败，[{}] 已存在", code);
        Storage storage = this.copy(storageReq);
        this.load(storage);
        this.save(storage);
        log.info("创建存储成功, {}", storage);
    }

    /**
     * 更新存储
     */
    public void update(StorageReq req, String id) {
        Storage oldStorage = getById(id);
        CheckUtils.throwIfNotEqual(req.getCode(), oldStorage.getCode(), "不允许修改存储编码");
        CheckUtils.throwIfNotEqual(req.getType(), oldStorage.getType(), "不允许修改存储类型");
        CommonStatusEnum newStatus = req.getStatus();
        CheckUtils.throwIf(Boolean.TRUE.equals(oldStorage.getDefaultStorage()) && CommonStatusEnum.DISABLED
                .equals(newStatus), "[{}] 是默认存储，不允许禁用", oldStorage.getName());
        CommonStatusEnum oldStatus = oldStorage.getStatus();
        if (Boolean.TRUE.equals(req.getDefaultStorage())) {
            CheckUtils.throwIf(!CommonStatusEnum.ENABLED.equals(oldStatus), "请先启用该存储");
            CheckUtils.throwIf(this.isDefaultExists(id), "请先取消原有默认存储");
        }

        Storage newStorage = copy(req, oldStorage);
        updateById(newStorage);

        // 先卸载
        if (CommonStatusEnum.ENABLED.equals(oldStatus)) {
            this.unload(oldStorage);
        }
        // 再加载
        if (CommonStatusEnum.ENABLED.equals(newStatus)) {
            this.load(oldStorage);
        }

        log.info("更新存储成功, {}", newStatus);
    }

    /**
     * 创建存储
     */
    public void delete(List<String> ids) {
        CheckUtils.throwIf(fileService.countByStorageIds(ids) > 0, "所选存储存在文件关联，请删除文件后重试");
        List<Storage> storageList = queryChain().where(STORAGE.ID.in(ids)).list();
        storageList.forEach(s -> {
            CheckUtils.throwIfEqual(Boolean.TRUE, s.getDefaultStorage(), "[{}] 是默认存储，不允许禁用", s.getName());
            // 卸载启用状态的存储
            if (CommonStatusEnum.ENABLED.equals(s.getStatus())) {
                this.unload(s);
            }
        });
        log.info("删除存储成功, {}", storageList);
    }

    /**
     * 查询默认存储
     *
     * @return 存储信息
     */
    public Storage getDefaultStorage() {
        return this.queryChain().where(STORAGE.DEFAULT_STORAGE.eq(true)).one();
    }

    /**
     * 根据编码查询
     *
     * @param code 编码
     * @return 存储信息
     */
    public Storage getByCode(String code) {
        return this.queryChain().where(STORAGE.CODE.eq(code)).one();
    }

    /**
     * 加载存储
     *
     * @param storage 存储信息
     */
    public void load(Storage storage) {
        CopyOnWriteArrayList<FileStorage> fileStorageList = fileStorageService.getFileStorageList();
        String domain = storage.getDomain();
        ValidationUtils.throwIf(!HttpUtil.isHttp(domain) || HttpUtil.isHttps(domain), "域名格式错误");
        String bucketName = storage.getBucketName();
        StorageTypeEnum type = storage.getType();
        if (StorageTypeEnum.LOCAL.equals(type)) {
            ValidationUtils.validate(storage, ValidateGroup.Storage.Local.class);
            storage.setBucketName(StrUtil.appendIfMissing(bucketName.replace(StrUtil.BACKSLASH, StrUtil.SLASH), StrUtil.SLASH));
            FileStorageProperties.LocalPlusConfig config = new FileStorageProperties.LocalPlusConfig();
            config.setPlatform(storage.getCode());
            config.setStoragePath(bucketName);
            fileStorageList.addAll(FileStorageServiceBuilder.buildLocalPlusFileStorage(Collections.singletonList(config)));
            SpringWebUtils.registerResourceHandler(MapUtil.of(URLUtil.url(storage.getDomain()).getPath(), bucketName));
        } else if (StorageTypeEnum.S3.equals(type)) {
            ValidationUtils.validate(storage, ValidateGroup.Storage.S3.class);
            FileStorageProperties.AmazonS3Config config = new FileStorageProperties.AmazonS3Config();
            config.setPlatform(storage.getCode());
            config.setAccessKey(storage.getAccessKey());
            config.setSecretKey(storage.getSecretKey());
            config.setEndPoint(storage.getEndpoint());
            config.setBucketName(bucketName);
            config.setDomain(domain);
            fileStorageList.addAll(FileStorageServiceBuilder.buildAmazonS3FileStorage(Collections.singletonList(config), null));
        }
    }

    /**
     * 卸载存储
     *
     * @param storage 存储信息
     */
    public void unload(Storage storage) {
        CopyOnWriteArrayList<FileStorage> fileStorageList = fileStorageService.getFileStorageList();
        FileStorage fileStorage = fileStorageService.getFileStorage(storage.getCode());
        fileStorageList.remove(fileStorage);
        fileStorage.close();
        SpringWebUtils.deRegisterResourceHandler(MapUtil.of(URLUtil.url(storage.getDomain()).getPath(), storage.getBucketName()));
    }

    /**
     * 默认存储是否存在
     *
     * @param selfId 排除自身id
     * @return 是否存在
     */
    private boolean isDefaultExists(String selfId) {
        return this.queryChain()
                .where(STORAGE.DEFAULT_STORAGE.eq(true))
                .and(STORAGE.ID.ne(selfId))
                .exists();
    }

    /**
     * 编码是否存在
     *
     * @param code 编码
     * @param selfId 排除自身id
     * @return 是否存在
     */
    private boolean isCodeExists(String code, String selfId) {
        return this.queryChain()
                .where(STORAGE.CODE.eq(code))
                .and(STORAGE.ID.ne(selfId))
                .exists();
    }
}
